/* 
 * Copyright 2010 Ziya Guven Koylu, Ozge Koylu
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.koylu.caffein.proc;

import com.koylu.caffein.converter.Converter;
import com.koylu.caffein.converter.ConverterManager;
import com.koylu.caffein.exception.ClazzConfigException;
import com.koylu.caffein.model.caffein.CaffeinConfig;

public class Param {
	private Proc proc;
	private int index;
	private String name;
	private String column;
	private String type = null;/*Java Field Type*/
	private Integer columnDataType = null;/*Column SQL Data Type*/
	private Integer columnType = null;/*Column Type IN,OUT,INOUT*/
	private Boolean columnNullable = null;
	private String converter;
	
	public int getIndex() {
		return index;
	}
	
	public void setIndex(int index) {
		this.index = index;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getColumn() {
		return column;
	}
	
	public void setColumn(String column) {
		this.column = column;
	}
	
	public Proc getProc() {
		return proc;
	}
	
	public void setProc(Proc proc) {
		this.proc = proc;
	}
	
	public String getType() {
		return type;
	}
	
	public void setType(String type) {
		this.type = type;
	}
	
	public void setColumnDataType(String columnDataTypeName) throws Exception{
		if( !"".equals(columnDataTypeName) && columnDataTypeName != null ){
			columnDataType = ConverterManager.getJdbcTypeValue(columnDataTypeName);
			if(columnDataType == null){
				throw new ClazzConfigException("column-data-type not found:"+columnDataTypeName+"for proc:"+proc.getProcedureWithSchema()+" column:"+getColumn());
			}			
		}
	}
	
	public void setColumnDataType(int columnDataType) throws Exception{
		this.columnDataType = columnDataType;
	}
	
	public int getColumnDataType(CaffeinConfig caffeinConfig) throws Exception {
		if(columnDataType == null){
			try {
				columnDataType = caffeinConfig.getProcColumnDataType(proc.getProcedureWithSchema(), getColumn());
			} catch (Exception e) {
				throw new ClazzConfigException("column or table not found for proc:"+proc.getProcedureWithSchema()+" column:"+getColumn(), e);
			}
		}
		return columnDataType;
	}

	public void setColumnType(String columnTypeName) throws Exception{
		if( !"".equals(columnTypeName) && columnTypeName != null ){
			try {
				if("IN".equalsIgnoreCase(columnTypeName)){
					columnType = 1;
				}else if("OUT".equalsIgnoreCase(columnTypeName)){
					columnType = 4;
				}else if("INOUT".equalsIgnoreCase(columnTypeName)){
					columnType = 2;
				}else if("RETURN".equals(columnTypeName)){
					columnType = 5;
				}
				if(columnType == null){
					throw new ClazzConfigException("column-type not found:"+columnTypeName+"for proc:"+proc.getProcedureWithSchema()+" column:"+getColumn());
				}
			} catch (Exception e) {
				throw e;
			}			
		}
	}
	
	public void setColumnType(int columnType) throws Exception{
		this.columnType = columnType;
	}
	
	public int getColumnType(CaffeinConfig caffeinConfig) throws Exception {
		if(columnType == null){
			try {
				columnType = caffeinConfig.getProcColumnType(proc.getProcedureWithSchema(), getColumn());
			} catch (Exception e) {
				throw new ClazzConfigException("column or table not found for proc:"+proc.getProcedureWithSchema()+" column:"+getColumn(), e);
			}
		}
		return columnType;
	}
	
	public void setColumnNullable(boolean columnNullable) throws Exception{
		this.columnNullable = columnNullable;
	}
	
	public boolean isColumnNullable(CaffeinConfig caffeinConfig) throws Exception {
		if(columnNullable == null){
			try {
				columnNullable = caffeinConfig.isProcColumnNullable(proc.getProcedureWithSchema(), getColumn());
			} catch (Exception e) {
				throw new ClazzConfigException("column or table not found for proc:"+proc.getProcedureWithSchema()+" column:"+getColumn(), e);
			}
		}
		return columnNullable;
	}
	
	public String getConverter() {
		return converter;
	}

	public void setConverter(String converter) {
		this.converter = converter;
	}
	
	private Class<?> realClass;
	public Class<?> getRealClass(CaffeinConfig caffeinConfig) throws Exception {
		if(realClass == null){
			if(virtualClass == null){
				realClass = CaffeinConfig.getFieldType(proc.getRealClass(caffeinConfig), name);
			}else{
				realClass = virtualClass;
			}			
		}
		return realClass;
	}
	
	private Class<?> virtualClass;
	public Class<?> toVirtualClass(CaffeinConfig caffeinConfig) throws Exception {
		if(virtualClass == null){
			if( type == null || "".equals(type)  ){
				int columnDataType = getColumnDataType(caffeinConfig);
				if(ConverterManager.getTypeMapForVirtualClass().containsKey(columnDataType)){
					virtualClass = ConverterManager.getTypeMapForVirtualClass().get(columnDataType);
				}else{
					throw new ClazzConfigException("no predefined type found for column type:"+ConverterManager.getJdbcTypeName(columnDataType));
				}
			}else{
				try {
					virtualClass = Class.forName(type);	
				} catch (Exception e) {
					throw new ClazzConfigException("property type not found:"+type, e);
				}				
			}
		}
		return virtualClass;
	}
	
	private Converter converterObject;
	public Converter getConverter(CaffeinConfig caffeinConfig) throws Exception {
		if (converterObject == null) {
			int columnType = getColumnDataType(caffeinConfig);
			Class<?> fieldType = getRealClass(caffeinConfig);
			if(fieldType.isPrimitive()){
				Class<?> temp = ConverterManager.getPrimitiveMap().get(fieldType);
				if(temp != null){
					fieldType = temp;
				}
			}
			if (converter != null && !"".equals(converter)) {
				try {
					converterObject = (Converter) Class.forName(converter).newInstance();
					converterObject.setJdbcType(columnType);
					converterObject.setFieldType(fieldType);
				} catch (Exception e) {
					throw new ClazzConfigException("invalid converter class:"+converter, e);
				}
			} else {
				converterObject = ConverterManager.getConverterObject(columnType, fieldType);
				if(converterObject == null){
					throw new ClazzConfigException("converter not found for columnType:"+ConverterManager.getJdbcTypeName(columnType)+" fieldType:"+fieldType.getCanonicalName());
				}
			}
		}
		return converterObject;
	}
	
	public String toString() {
		return "Index:"+getIndex() + " Name:" +getName() + " Column:" + getColumn() +" Converter:"+getConverter()+" Type:"+getType();
	}
}